#pragma once
#include "stdafx.h"
#include "Application.h"
#include "MonkeyScene.h"
#include "Menu.h"
#include "TimerManager.h"

#include "SceneController.h"
#include "EventListeners.h"
#include "EventHandlers.h"

#include <HSModel/Model3DStorage.h>
#include <HSModel/World.h>
#include <HSModel/Picker.h>
#include <HSModel/ObjParser.h>
#include <HSModel/SharedConstants.h>
#include <HSModel/Ground.h>
#include <HSModel/Landscape.h>
#include <HSModel/WorldDumper.h>
#include <HSModel/GameController.h>
#include <HSModel/GroundPickingOperator.h>
#include <HSModel/WorldUpdater.h>

Application::Application()
  {

  }

Application::~Application()
  {
  }

void Application::Start()
  {
  m_shutdown = false;

  mp_ogre.reset(new OgreFramework());
  mp_ogre->Initialize();

  //Initialize CEGUI
  mp_cegui_framework.reset(new CEGUIFramework(*mp_ogre));
  mp_cegui_framework->InitializeCEGUI();

  // Creating own OverlaySystem
  mp_overlay.reset(new Ogre::OverlaySystem());

  mp_input_manager.reset(new InputManager(*mp_ogre));
  // TODO: remove later
  mp_input_manager->AddSubscriber(std::make_shared<CeguiEventListener>(*this));

  _InitGame();

  // Menu creation
  mp_menu.reset(new Menu(*this, *mp_game_controller, true));

  _CreateInitialScene();
  mp_ogre->GetRoot()->addFrameListener(this);
  mp_ogre->GetRoot()->startRendering();
  }
void Application::Shutdown()
  {
  m_shutdown = true;
  }

bool Application::frameRenderingQueued(const Ogre::FrameEvent& evt)
  {
  if(m_shutdown)
    return false;
  float sleepTime = 0.018 - evt.timeSinceLastFrame;
  sleepTime *= 1000.0f;
  if(0 < sleepTime)
    Sleep(sleepTime);

  if(mp_ogre->GetKeyboard()->isKeyDown(OIS::KC_ESCAPE))
    {
    mp_menu->ResetButtonCaptions();
    mp_event_handler.reset();
    }
  if(mp_ogre->GetKeyboard()->isKeyDown(OIS::KC_F4))
    {
    return false;
    }

  mp_input_manager->Update(evt.timeSinceLastFrame);
  mp_monkey_scene->CameraMan().FrameRenderingQueued(evt);

  mp_input_manager->Update(evt.timeSinceLastFrame);

  return true;
  }

void Application::_InitModel3DStorage()
  {
  mp_model_storage.reset(new Model3DStorage());

  // DEFAULT_TREE models
  std::shared_ptr<Model3D> p_default_tree_model0(new Model3D());
  ImportObj(*p_default_tree_model0, L"..//..//Source//Resources//obj//tree1_0.obj"); 
  p_default_tree_model0->SetMaterialName("Tree/Tree1");
  mp_model_storage->AddModel("DEFAULT_TREE_0", p_default_tree_model0, ModelSizeConstants::TREE_DEFAULT_SIZE);

  std::shared_ptr<Model3D> p_default_tree_model1(new Model3D());
  ImportObj(*p_default_tree_model1, L"..//..//Source//Resources//obj//tree1_1.obj"); 
  p_default_tree_model1->SetMaterialName("Tree/Tree1");
  mp_model_storage->AddModel("DEFAULT_TREE_1", p_default_tree_model1, ModelSizeConstants::TREE_DEFAULT_SIZE);

  std::shared_ptr<Model3D> p_default_tree_model2(new Model3D());
  ImportObj(*p_default_tree_model2, L"..//..//Source//Resources//obj//tree1_2.obj"); 
  p_default_tree_model2->SetMaterialName("Tree/Tree1");
  mp_model_storage->AddModel("DEFAULT_TREE_2", p_default_tree_model2, ModelSizeConstants::TREE_DEFAULT_SIZE);

  // DEFAULT_HUMAN models
  std::shared_ptr<Model3D> p_default_human_model0(new Model3D());
  ImportObj(*p_default_human_model0, L"..//..//Source//Resources//obj//girl_0.obj");
  p_default_human_model0->SetMaterialName("Human/Girl");
  mp_model_storage->AddModel("DEFAULT_HUMAN_0", p_default_human_model0, ModelSizeConstants::HUMAN_DEFAULT_SIZE);

  std::shared_ptr<Model3D> p_default_human_model1(new Model3D());
  ImportObj(*p_default_human_model1, L"..//..//Source//Resources//obj//girl_1.obj");
  p_default_human_model1->SetMaterialName("Human/Girl");
  mp_model_storage->AddModel("DEFAULT_HUMAN_1", p_default_human_model1, ModelSizeConstants::HUMAN_DEFAULT_SIZE);

  // BLUE_BALLOON models
  std::shared_ptr<Model3D> p_blue_balloon_model0(new Model3D());
  ImportObj(*p_blue_balloon_model0, L"..//..//Source//Resources//obj//balloon_0.obj");
  p_blue_balloon_model0->SetMaterialName("Balloons/BlueBalloon");
  mp_model_storage->AddModel("BLUE_BALLOON_0", p_blue_balloon_model0, ModelSizeConstants::BALLOON_DEFAULT_SIZE, false);
  
  std::shared_ptr<Model3D> p_blue_balloon_model1(new Model3D());
  ImportObj(*p_blue_balloon_model1, L"..//..//Source//Resources//obj//balloon_1.obj");
  p_blue_balloon_model1->SetMaterialName("Balloons/BlueBalloon");
  mp_model_storage->AddModel("BLUE_BALLOON_1", p_blue_balloon_model1, ModelSizeConstants::BALLOON_DEFAULT_SIZE, false);
  
  // YELLOW_BALLOON models
  std::shared_ptr<Model3D> p_yellow_balloon_model0(new Model3D());
  ImportObj(*p_yellow_balloon_model0, L"..//..//Source//Resources//obj//balloon_0.obj");
  p_yellow_balloon_model0->SetMaterialName("Balloons/YellowBalloon");
  mp_model_storage->AddModel("YELLOW_BALLOON_0", p_yellow_balloon_model0, ModelSizeConstants::BALLOON_DEFAULT_SIZE, false);
  
  std::shared_ptr<Model3D> p_yellow_balloon_model1(new Model3D());
  ImportObj(*p_yellow_balloon_model1, L"..//..//Source//Resources//obj//balloon_1.obj");
  p_yellow_balloon_model1->SetMaterialName("Balloons/YellowBalloon");
  mp_model_storage->AddModel("YELLOW_BALLOON_1", p_yellow_balloon_model1, ModelSizeConstants::BALLOON_DEFAULT_SIZE, false);

  
  // DEFAULT_FERRIS_WHEEL models
  std::shared_ptr<Model3D> p_default_ferris_wheel_model0(new Model3D());
  ImportObj(*p_default_ferris_wheel_model0, L"..//..//Source//Resources//obj//FerrisWheel1.obj");
  p_default_ferris_wheel_model0->SetMaterialName("FerrisWheel/FerrisWheel1");
  mp_model_storage->AddModel("DEFAULT_FERRIS_WHEEL_0", p_default_ferris_wheel_model0, ModelSizeConstants::FERRIS_WHEEL_DEFAULT_SIZE);
  
  std::shared_ptr<Model3D> p_default_ferris_wheel_model1(new Model3D());
  ImportObj(*p_default_ferris_wheel_model1, L"..//..//Source//Resources//obj//FerrisWheel3.obj");
  p_default_ferris_wheel_model1->SetMaterialName("FerrisWheel/FerrisWheel1");
  mp_model_storage->AddModel("DEFAULT_FERRIS_WHEEL_1", p_default_ferris_wheel_model1, ModelSizeConstants::FERRIS_WHEEL_DEFAULT_SIZE);
  
  std::shared_ptr<Model3D> p_default_ferris_wheel_model2(new Model3D());
  ImportObj(*p_default_ferris_wheel_model2, L"..//..//Source//Resources//obj//FerrisWheel2.obj");
  p_default_ferris_wheel_model2->SetMaterialName("FerrisWheel/FerrisWheel1");
  mp_model_storage->AddModel("DEFAULT_FERRIS_WHEEL_2", p_default_ferris_wheel_model2, ModelSizeConstants::FERRIS_WHEEL_DEFAULT_SIZE);

  // DEFAULT_BENCH model
  std::shared_ptr<Model3D> p_default_bench_model(new Model3D());
  ImportObj(*p_default_bench_model, L"..//..//Source//Resources//obj//bench_0.obj");
  p_default_bench_model->SetMaterialName("Benches/WoodenBench");
  mp_model_storage->AddModel("DEFAULT_BENCH_0", p_default_bench_model, ModelSizeConstants::BENCH_DEFAULT_SIZE);
  
  // DEFAULT_BALLOONS_SHOP models
  std::shared_ptr<Model3D> p_default_balloons_shop_model0(new Model3D());
  ImportObj(*p_default_balloons_shop_model0, L"..//..//Source//Resources//obj//BalloonsShop.obj");
  p_default_balloons_shop_model0->SetMaterialName("BalloonsShop/Shop");
  mp_model_storage->AddModel("DEFAULT_BALLOONS_SHOP_0", p_default_balloons_shop_model0, ModelSizeConstants::BALLOONS_SHOP_DEFAULT_SIZE); 
  }

void Application::_CreateInitialScene()
  {
  mp_monkey_scene = std::make_shared<MonkeyScene>(*mp_ogre, mp_overlay);

  _InitModel3DStorage();
  
  Ogre::SceneManager* p_scene_manager = mp_ogre -> GetSceneManager("MonkeySceneManager");
  mp_world.reset(new World());
	ResetPicker();

  mp_scene_controller.reset(new SceneController(p_scene_manager, mp_ogre->GetRoot()->getMeshManager(), *mp_world, *mp_model_storage));
  mp_world_updater.reset(new WorldUpdater(*mp_world, *mp_game_controller));
  mp_scene_controller->RenderWorld();
  mp_input_manager->AddSubscriber(mp_monkey_scene);

  mp_timer_manager.reset(new TimerManager<WorldUpdater>());
  mp_timer_manager->AddTimer(2400u, &WorldUpdater::MakeProfit, mp_world_updater.get(), 0 /* timer id */, true /* repeat */, true);
  mp_timer_manager->AddTimer(300u, &WorldUpdater::ProcessAnimations, mp_world_updater.get(), 1 /* timer id */, true /* repeat */, true);
  mp_timer_manager->AddTimer(120u, &WorldUpdater::MoveObjects, mp_world_updater.get(), 2 /* timer id */, true /* repeat */, true);
  mp_ogre->GetRoot()->addFrameListener(mp_timer_manager.get());
  }

void Application::ResetPicker()
	{
  mp_picker.reset(new Picker());

  const GroundPickingOperator* p_container = new GroundPickingOperator(mp_world->GetGround()->GetHeightMap());

  mp_picker->AddObject(p_container, mp_world->GetGround()->GetLandscape().GetPosition());
  }


void Application::_InitGame()
  {
  mp_game_controller.reset(new GameController());
  }

OgreFramework& Application::GetOgre() const
  {
  return *mp_ogre;
  }

World& Application::GetWorld() const
  {
  return *mp_world;
  }

SceneController& Application::GetSceneController() const
  {
  return *mp_scene_controller;
  }

GameController& Application::GetGameController() const
  {
  return *mp_game_controller;
  }

Picker& Application::GetPicker() const
  {
  return *mp_picker;
  }

WorldUpdater& Application::GetWorldUpdater() const
  {
  return *mp_world_updater;
  }

bool Application::IsEventHandler(const std::string &i_handler_name) const
  {
  if(!mp_event_handler)
    return false;

  return mp_event_handler->GetName() == i_handler_name;
  }

const Menu& Application::GetMenu() const
  {
  return *mp_menu;
  }

void Application::ResetHandler()
  {
  mp_event_handler.reset();
  }

void Application::ResetHandler(std::shared_ptr<EventHandler> i_handler)
  {
  mp_event_handler = i_handler;
  }

void Application::RaiseClickEvent(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {
  if (mp_event_handler)
    {
    mp_event_handler->MouseClick(evt, id);
    }	
  }

void Application::RaiseMouseUpEvent(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
  {
  if (mp_event_handler)
    {
    mp_event_handler->MouseUp(evt, id);
    }	
  }

void Application::RaiseMoveEvent(const OIS::MouseEvent &evt)
  {
  if (mp_event_handler)
    {
    mp_event_handler->MouseMove(evt);
    }	
  }

const Model3DStorage& Application::GetModelStorage() const
  {
  return *mp_model_storage;
  }
